// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Manage the defer stack.

#include <stddef.h>
#include <stdlib.h>

#include "runtime_types.h"
#include "proc.h"
#include "panic.h"
#include "defer.h"
#include "mem_alloc.h"

// TODO: A library for stack unwinding is missing at the moment, so the runtime currently just
// ignores recover() and kills the fibril instead of restoring execution.

// This function is called each time we need to defer a call.

void __go_defer(_Bool *frame, void (*pfn) (void *), void *arg)
{
	G *g;
	struct __go_defer_stack *n;

	g = runtime_g();
	n = (struct __go_defer_stack *)runtime_malloc(sizeof(struct __go_defer_stack));
	n->__next = g->defer;
	n->__frame = frame;
	n->__panic = g->panic;
	n->__pfn = pfn;
	n->__arg = arg;
	n->__retaddr = NULL;
	g->defer = n;
}

// This function is called when we want to undefer the stack.

void __go_undefer(_Bool *frame)
{
	G *g;

	g = runtime_g();
	while (g->defer != NULL && g->defer->__frame == frame) {
		struct __go_defer_stack *d;
		void (*pfn) (void *);

		d = g->defer;
		pfn = d->__pfn;
		d->__pfn = NULL;

		if (pfn != NULL)
			(*pfn) (d->__arg);

		g->defer = d->__next;
		runtime_free(d);

		// Since we are executing a defer function here, we know we are
		// returning from the calling function.  If the calling
		// function, or one of its callees, paniced, then the defer
		// functions would be executed by __go_panic.
		*frame = 1;
	}
}

// This function is called to record the address to which the deferred
// function returns.  This may in turn be checked by __go_can_recover.
// The frontend relies on this function returning false.

_Bool __go_set_defer_retaddr(void *retaddr)
{
	G *g = runtime_g();
	if (g->defer != NULL)
		g->defer->__retaddr = retaddr;
	return 0;
}

// This function is called by exception handlers used when unwinding
// the stack after a recovered panic.  The exception handler looks
// like this:
// 	__go_check_defer (frame);
//	return;
// If we have not yet reached the frame we are looking for, we
// continue unwinding.
extern void __go_check_defer(_Bool * frame);

void
__go_check_defer(_Bool * frame)
{
	// Should never be called.
	abort();
}

void _Unwind_Resume(void);

void
_Unwind_Resume(void)
{
	// Should never be called.
	abort();
}
